<?php

namespace App\Common\Libary\Third;

use App\Common\Libary\Third\Request\PddRequest\PddDdkAuthorityQueryRequest;
use App\Common\Libary\Third\Request\PddRequest\PddDdkRpPromUrlGenerateRequest;
use App\Setting\Service\SettingConfigService;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Codec\Packer\JsonPacker;
use Hyperf\Contract\PackerInterface;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Guzzle\CoroutineHandler;
use Mine\Exception\NormalStatusException;
use function Hyperf\Collection\data_get;

class Pdd
{
    protected Client $client;

    protected PackerInterface $packer;

    protected string $baseUri = 'http://gw-api.pinduoduo.com/api/router';

    private string $appKey;

    private string $secretKey;

    private string $accessToken;

    private string $pid;

    private array $externalInfo = [];

    private bool $throwError = false;

    public function __construct()
    {
        $handler = null;
        if (Coroutine::inCoroutine()) {
            $handler = new CoroutineHandler();
        }

        $this->client = new Client([
            'base_uri' => $this->baseUri,
            'timeout' => 3,
            'handler' => $handler,
        ]);
        $this->packer = new JsonPacker();

        $config = container()->get(SettingConfigService::class)->getConfigMapByGroupKey('affiliate_oauth_config');

        $this->appKey = $config['pdd_app_key'];
        $this->secretKey = $config['pdd_app_secret'];
    }

    public function transferChain($content ,$urls)
    {
        foreach ($urls as $url){
            $res = $this->transformByUrl($url);
            if($res === false){
               continue;
            }

            $clickUrl = data_get($res,'goods_zs_unit_generate_response.mobile_short_url');
            if($clickUrl){
                $content = str_replace($url,$clickUrl,$content);
            }
        }
        return $content;
    }

    public function transformByUrl($url ,$generate_short_link = true): mixed
    {
        $param['pid'] = $this->pid;
        $param['source_url'] = $url;

        $param['custom_parameters'] = $this->externalInfo;
        $param['generate_short_link'] = $generate_short_link;

        $param = $this->paramsHandle($param);

        $resp = $this->execute('pdd.ddk.oauth.goods.zs.unit.url.gen' ,$param);
        return $resp;
    }

    /**
     * 生成营销工具推广链接
     * @param PddDdkRpPromUrlGenerateRequest $data
     * @return false|mixed
     */
    public function promUrlGenerate(PddDdkRpPromUrlGenerateRequest $data):  mixed
    {
        $params = $data->toArray();
        $params = $this->paramsHandle($params);
        return $this->execute('pdd.ddk.oauth.rp.prom.url.generate' ,$params);
    }

    /**
     * 检查PID是否已授权
     * @param PddDdkAuthorityQueryRequest $data
     * @return bool
     */
    public function checkPidHasOauth(PddDdkAuthorityQueryRequest $data):  bool
    {
        $params = $data->toArray();
        $params = $this->paramsHandle($params);
        $response =  $this->execute('pdd.ddk.member.authority.query' ,$params);
        if(isset($response['authority_query_response']['bind']) && $response['authority_query_response']['bind']){
            return true;
        }else{
            return  false;
        }
    }

    /**
     * 查询是否绑定备案
     * 文档地址：https://jinbao.pinduoduo.com/third-party/api-detail?apiName=pdd.ddk.member.authority.query
     * @param string $pid
     * @param array $customParameters
     * @return array
     */
    public function authorityQuery(string $pid,array $customParameters = []): array
    {
        $param['pid'] = $pid;
        $param['custom_parameters'] = $customParameters;
        $param = $this->paramsHandle($param);
        $resp = $this->execute('pdd.ddk.oauth.member.authority.query' ,$param);
        return $resp;
    }


    private function execute(string $apiType,array $params)
    {
        $params['client_id'] = $this->appKey;
        $params['type'] = $apiType;
        $params['access_token'] = $this->accessToken;
        $params['data_type'] = 'JSON';
        $params['timestamp'] =$this->getMillisecond();

        $sign = $this->signature($params);    //  生成签名    MD5加密转大写
        $params['sign'] = $sign;
        return $this->post($params);
    }

    public function setAccessToken(string $accessToken): Pdd
    {
        $this->accessToken = $accessToken;
        return $this;
    }

    public function setPid(string $pid): Pdd
    {
        $this->pid = $pid;
        return $this;
    }

    public function setExternalInfo(array $externalInfo): Pdd
    {
        $this->externalInfo = $externalInfo;
        return $this;
    }

    public function setThrowError(bool $throwError): Pdd
    {
        $this->throwError = $throwError;
        return $this;
    }


    private function post(array $params)
    {
        try {
            $response = $this->client->post('', [
                'form_params' => $params
            ]);
            $data = $this->packer->unpack((string) $response->getBody());
            if(isset($data['error_response'])){
                if($this->throwError){
                    throw new NormalStatusException($data['error_response']['error_msg']);
                }
                logger()->info("pdd{$params['type']}请求失败:".$data['error_response']['error_msg']);
                return false;
            }
            return $data;
        } catch (GuzzleException $e) {
            if($this->throwError){
                throw new NormalStatusException('请求失败'.$e->getMessage());
            }
            return false;
        }
    }

    /**
     * @param array $params
     *
     * @return array
     */
    private function paramsHandle(array $params): array
    {
        array_walk($params, function (&$item) {
            if (is_array($item)) {
                $item = json_encode($item);
            }
            if (is_bool($item)) {
                $item = ['false', 'true'][intval($item)];
            }
        });

        return $params;
    }

    private function signature($params): string
    {
        ksort($params);
        $paramsStr = '';
        array_walk($params, function ($item, $key) use (&$paramsStr) {
            if (!str_starts_with($item, '@')) {
                $paramsStr .= sprintf('%s%s', $key, $item);
            }
        });
        $secret = $this->secretKey;

        return strtoupper(md5(sprintf('%s%s%s', $secret, $paramsStr, $secret)));
    }

    private function getMillisecond(): string
    {
        list($t1, $t2) = explode(' ', microtime());
        return sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
    }
}